/******************************************************************************************************************************

 * @ Original idea by by Binny V A, Original version: 2.00.A
 * @ http://www.openjs.com/scripts/events/keyboard_shortcuts/
 * @ Original License : BSD

 * @ jQuery Plugin by Tzury Bar Yochay
 mail: tzury.by@gmail.com
 blog: evalinux.wordpress.com
 face: facebook.com/profile.php?id=513676303

 (c) Copyrights 2007

 * @ jQuery Plugin version Beta (0.0.2)
 * @ License: jQuery-License.

 TODO:
 add queue support (as in gmail) e.g. 'x' then 'y', etc.
 add mouse + mouse wheel events.

 USAGE:
 $.hotkeys.add('Ctrl+c', function(){ alert('copy anyone?');});
 $.hotkeys.add('Ctrl+c', {target:'div#editor', type:'keyup', propagate: true},function(){ alert('copy anyone?');});>
 $.hotkeys.remove('Ctrl+c');
 $.hotkeys.remove('Ctrl+c', {target:'div#editor', type:'keypress'});

 ******************************************************************************************************************************/

(function(jQuery) {
    this.version = '(beta)(0.0.3)';
    this.all = {};
    this.special_keys = {
        27 : 'esc',
        9 : 'tab',
        32 : 'space',
        13 : 'return',
        8 : 'backspace',
        145 : 'scroll',
        20 : 'capslock',
        144 : 'numlock',
        19 : 'pause',
        45 : 'insert',
        36 : 'home',
        46 : 'del',
        35 : 'end',
        33 : 'pageup',
        34 : 'pagedown',
        37 : 'left',
        38 : 'up',
        39 : 'right',
        40 : 'down',
        112 : 'f1',
        113 : 'f2',
        114 : 'f3',
        115 : 'f4',
        116 : 'f5',
        117 : 'f6',
        118 : 'f7',
        119 : 'f8',
        120 : 'f9',
        121 : 'f10',
        122 : 'f11',
        123 : 'f12'
    };
    this.shift_nums = {
        "`": "~",
        "1": "!",
        "2": "@",
        "3": "#",
        "4": "$",
        "5": "%",
        "6": "^",
        "7": "&",
        "8": "*",
        "9": "(",
        "0": ")",
        "-": "_",
        "=": "+",
        ";": ":",
        "'": "\"",
        ",": "<",
        ".": ">",
        "/": "?",
        "\\": "|"
    };
    this.add = function(combi, options, callback) {
        if (jQuery.isFunction(options)) {
            callback = options;
            options = {};
        }
        var opt = {},
            defaults = {
                type: 'keydown',
                propagate: false,
                disableInInput: false,
                target: jQuery('html')[0],
                checkParent: true
            },
            that = this;
        opt = jQuery.extend(opt, defaults, options || {});
        combi = combi.toLowerCase();
        var inspector = function(event) {
            event = jQuery.event.fix(event);
            var element = event.target;
            element = (element.nodeType == 3) ? element.parentNode: element;
            if (opt['disableInInput']) {
                var target = jQuery(element);
                if (target.is("input") || target.is("textarea")) {
                    return;
                }
            }
            var code = event.which,
                type = event.type,
                character = String.fromCharCode(code).toLowerCase(),
                special = that.special_keys[code],
                shift = event.shiftKey,
                ctrl = event.ctrlKey,
                alt = event.altKey,
                propagate = true,
                mapPoint = null;
            if (jQuery.browser.opera || jQuery.browser.safari || opt.checkParent) {
                while (!that.all[element] && element.parentNode) {
                    element = element.parentNode;
                }
            }
            var cbMap = that.all[element].events[type].callbackMap;
            if (!shift && !ctrl && !alt) {
                mapPoint = cbMap[special] || cbMap[character];
            } else {
                var modif = '';
                if (alt) modif += 'alt+';
                if (ctrl) modif += 'ctrl+';
                if (shift) modif += 'shift+';
                mapPoint = cbMap[modif + special] || cbMap[modif + character] || cbMap[modif + that.shift_nums[character]];
            }
            if (mapPoint) {
                mapPoint.cb(event);
                if (!mapPoint.propagate) {
                    event.stopPropagation();
                    event.preventDefault();
                    return false;
                }
            }
        };
        if (!this.all[opt.target]) {
            this.all[opt.target] = {
                events: {}
            };
        }
        if (!this.all[opt.target].events[opt.type]) {
            this.all[opt.target].events[opt.type] = {
                callbackMap: {}
            };
            jQuery.event.add(opt.target, opt.type, inspector);
        }
        this.all[opt.target].events[opt.type].callbackMap[combi] = {
            cb: callback,
            propagate: opt.propagate
        };
        return jQuery;
    };
    this.remove = function(exp, opt) {
        opt = opt || {};
        target = opt.target || jQuery('html')[0];
        type = opt.type || 'keydown';
        exp = exp.toLowerCase();
        delete this.all[target].events[type].callbackMap[exp];
        return jQuery;
    };
    jQuery.hotkeys = this;
    return jQuery;
})(jQuery);